"""文档处理工具 - 支持多种文档格式的解析和处理"""

import asyncio
import io
import re
from typing import Dict, Any, List, Optional, Union, Tuple
from pathlib import Path
from datetime import datetime
from loguru import logger

# 文档处理库
try:
    import PyPDF2
    from docx import Document
    from pptx import Presentation
    import openpyxl
except ImportError as e:
    logger.warning(f"文档处理库导入失败: {e}")

# 文本处理库
try:
    import nltk
    from langdetect import detect
except ImportError as e:
    logger.warning(f"文本处理库导入失败: {e}")


class DocumentProcessor:
    """文档处理器 - 支持多种格式的文档解析和文本提取"""
    
    def __init__(self):
        self.supported_formats = {
            '.txt': self._process_text,
            '.md': self._process_markdown,
            '.pdf': self._process_pdf,
            '.docx': self._process_docx,
            '.pptx': self._process_pptx,
            '.xlsx': self._process_xlsx,
            '.html': self._process_html,
            '.json': self._process_json
        }
        
        # 文本清理配置
        self.clean_patterns = [
            (r'\s+', ' '),  # 多个空白字符替换为单个空格
            (r'\n\s*\n', '\n\n'),  # 多个换行符替换为双换行
            (r'^\s+|\s+$', ''),  # 去除首尾空白
        ]
        
        # 语言检测缓存
        self._language_cache = {}
    
    async def process_document(self, content: Union[str, bytes], 
                             filename: Optional[str] = None,
                             file_type: Optional[str] = None) -> Dict[str, Any]:
        """处理文档并提取文本内容"""
        try:
            # 确定文件类型
            if file_type:
                ext = file_type.lower()
            elif filename:
                ext = Path(filename).suffix.lower()
            else:
                # 尝试从内容推断类型
                ext = await self._detect_file_type(content)
            
            if ext not in self.supported_formats:
                raise ValueError(f"不支持的文件格式: {ext}")
            
            # 处理文档
            processor = self.supported_formats[ext]
            result = await processor(content, filename)
            
            # 添加处理信息
            result.update({
                'file_type': ext,
                'filename': filename,
                'processed_at': datetime.utcnow().isoformat(),
                'processor_version': '1.0.0'
            })
            
            return result
            
        except Exception as e:
            logger.error(f"文档处理失败: {e}")
            return {
                'success': False,
                'error': str(e),
                'content': '',
                'metadata': {}
            }
    
    async def _detect_file_type(self, content: Union[str, bytes]) -> str:
        """检测文件类型"""
        if isinstance(content, str):
            # 文本内容检测
            if content.strip().startswith('<!DOCTYPE html') or '<html' in content[:100]:
                return '.html'
            elif content.strip().startswith('{') or content.strip().startswith('['):
                return '.json'
            elif '# ' in content or '## ' in content:
                return '.md'
            else:
                return '.txt'
        else:
            # 二进制内容检测
            if content.startswith(b'%PDF'):
                return '.pdf'
            elif content.startswith(b'PK\x03\x04'):
                # ZIP格式，可能是docx, pptx, xlsx
                if b'word/' in content[:1000]:
                    return '.docx'
                elif b'ppt/' in content[:1000]:
                    return '.pptx'
                elif b'xl/' in content[:1000]:
                    return '.xlsx'
            
            return '.txt'  # 默认为文本
    
    async def _process_text(self, content: Union[str, bytes], 
                          filename: Optional[str] = None) -> Dict[str, Any]:
        """处理纯文本文件"""
        try:
            if isinstance(content, bytes):
                # 尝试不同编码
                for encoding in ['utf-8', 'gbk', 'gb2312', 'latin-1']:
                    try:
                        text = content.decode(encoding)
                        break
                    except UnicodeDecodeError:
                        continue
                else:
                    text = content.decode('utf-8', errors='ignore')
            else:
                text = content
            
            # 清理文本
            cleaned_text = await self._clean_text(text)
            
            # 提取元数据
            metadata = await self._extract_text_metadata(cleaned_text)
            
            return {
                'success': True,
                'content': cleaned_text,
                'metadata': metadata
            }
            
        except Exception as e:
            logger.error(f"处理文本文件失败: {e}")
            raise
    
    async def _process_markdown(self, content: Union[str, bytes], 
                              filename: Optional[str] = None) -> Dict[str, Any]:
        """处理Markdown文件"""
        try:
            if isinstance(content, bytes):
                text = content.decode('utf-8')
            else:
                text = content
            
            # 提取Markdown结构
            headers = re.findall(r'^(#{1,6})\s+(.+)$', text, re.MULTILINE)
            
            # 清理Markdown标记
            cleaned_text = re.sub(r'#{1,6}\s+', '', text)  # 移除标题标记
            cleaned_text = re.sub(r'\*\*(.+?)\*\*', r'\1', cleaned_text)  # 移除粗体
            cleaned_text = re.sub(r'\*(.+?)\*', r'\1', cleaned_text)  # 移除斜体
            cleaned_text = re.sub(r'\[(.+?)\]\(.+?\)', r'\1', cleaned_text)  # 移除链接
            cleaned_text = re.sub(r'`(.+?)`', r'\1', cleaned_text)  # 移除代码标记
            
            cleaned_text = await self._clean_text(cleaned_text)
            
            # 提取元数据
            metadata = await self._extract_text_metadata(cleaned_text)
            metadata.update({
                'headers': [{'level': len(h[0]), 'text': h[1]} for h in headers],
                'format': 'markdown'
            })
            
            return {
                'success': True,
                'content': cleaned_text,
                'metadata': metadata
            }
            
        except Exception as e:
            logger.error(f"处理Markdown文件失败: {e}")
            raise
    
    async def _process_pdf(self, content: bytes, 
                         filename: Optional[str] = None) -> Dict[str, Any]:
        """处理PDF文件"""
        try:
            pdf_reader = PyPDF2.PdfReader(io.BytesIO(content))
            
            text_parts = []
            for page_num, page in enumerate(pdf_reader.pages):
                try:
                    page_text = page.extract_text()
                    if page_text.strip():
                        text_parts.append(page_text)
                except Exception as e:
                    logger.warning(f"提取PDF第{page_num+1}页失败: {e}")
            
            full_text = '\n\n'.join(text_parts)
            cleaned_text = await self._clean_text(full_text)
            
            # 提取元数据
            metadata = await self._extract_text_metadata(cleaned_text)
            metadata.update({
                'pages': len(pdf_reader.pages),
                'format': 'pdf'
            })
            
            # 尝试获取PDF元数据
            try:
                pdf_info = pdf_reader.metadata
                if pdf_info:
                    metadata.update({
                        'title': pdf_info.get('/Title', ''),
                        'author': pdf_info.get('/Author', ''),
                        'subject': pdf_info.get('/Subject', ''),
                        'creator': pdf_info.get('/Creator', '')
                    })
            except Exception:
                pass
            
            return {
                'success': True,
                'content': cleaned_text,
                'metadata': metadata
            }
            
        except Exception as e:
            logger.error(f"处理PDF文件失败: {e}")
            raise
    
    async def _process_docx(self, content: bytes, 
                          filename: Optional[str] = None) -> Dict[str, Any]:
        """处理Word文档"""
        try:
            doc = Document(io.BytesIO(content))
            
            text_parts = []
            for paragraph in doc.paragraphs:
                if paragraph.text.strip():
                    text_parts.append(paragraph.text)
            
            # 处理表格
            for table in doc.tables:
                for row in table.rows:
                    row_text = ' | '.join(cell.text.strip() for cell in row.cells)
                    if row_text.strip():
                        text_parts.append(row_text)
            
            full_text = '\n\n'.join(text_parts)
            cleaned_text = await self._clean_text(full_text)
            
            # 提取元数据
            metadata = await self._extract_text_metadata(cleaned_text)
            metadata.update({
                'paragraphs': len(doc.paragraphs),
                'tables': len(doc.tables),
                'format': 'docx'
            })
            
            # 尝试获取文档属性
            try:
                core_props = doc.core_properties
                metadata.update({
                    'title': core_props.title or '',
                    'author': core_props.author or '',
                    'subject': core_props.subject or '',
                    'created': core_props.created.isoformat() if core_props.created else '',
                    'modified': core_props.modified.isoformat() if core_props.modified else ''
                })
            except Exception:
                pass
            
            return {
                'success': True,
                'content': cleaned_text,
                'metadata': metadata
            }
            
        except Exception as e:
            logger.error(f"处理Word文档失败: {e}")
            raise
    
    async def _process_pptx(self, content: bytes, 
                          filename: Optional[str] = None) -> Dict[str, Any]:
        """处理PowerPoint文档"""
        try:
            prs = Presentation(io.BytesIO(content))
            
            text_parts = []
            for slide_num, slide in enumerate(prs.slides):
                slide_text = []
                
                # 提取文本框内容
                for shape in slide.shapes:
                    if hasattr(shape, 'text') and shape.text.strip():
                        slide_text.append(shape.text)
                
                if slide_text:
                    text_parts.append(f"幻灯片 {slide_num + 1}:\n" + '\n'.join(slide_text))
            
            full_text = '\n\n'.join(text_parts)
            cleaned_text = await self._clean_text(full_text)
            
            # 提取元数据
            metadata = await self._extract_text_metadata(cleaned_text)
            metadata.update({
                'slides': len(prs.slides),
                'format': 'pptx'
            })
            
            return {
                'success': True,
                'content': cleaned_text,
                'metadata': metadata
            }
            
        except Exception as e:
            logger.error(f"处理PowerPoint文档失败: {e}")
            raise
    
    async def _process_xlsx(self, content: bytes, 
                          filename: Optional[str] = None) -> Dict[str, Any]:
        """处理Excel文档"""
        try:
            workbook = openpyxl.load_workbook(io.BytesIO(content), data_only=True)
            
            text_parts = []
            for sheet_name in workbook.sheetnames:
                sheet = workbook[sheet_name]
                sheet_text = [f"工作表: {sheet_name}"]
                
                for row in sheet.iter_rows(values_only=True):
                    row_text = ' | '.join(str(cell) if cell is not None else '' for cell in row)
                    if row_text.strip():
                        sheet_text.append(row_text)
                
                if len(sheet_text) > 1:  # 除了标题外还有内容
                    text_parts.append('\n'.join(sheet_text))
            
            full_text = '\n\n'.join(text_parts)
            cleaned_text = await self._clean_text(full_text)
            
            # 提取元数据
            metadata = await self._extract_text_metadata(cleaned_text)
            metadata.update({
                'sheets': len(workbook.sheetnames),
                'sheet_names': workbook.sheetnames,
                'format': 'xlsx'
            })
            
            return {
                'success': True,
                'content': cleaned_text,
                'metadata': metadata
            }
            
        except Exception as e:
            logger.error(f"处理Excel文档失败: {e}")
            raise
    
    async def _process_html(self, content: Union[str, bytes], 
                          filename: Optional[str] = None) -> Dict[str, Any]:
        """处理HTML文件"""
        try:
            if isinstance(content, bytes):
                text = content.decode('utf-8')
            else:
                text = content
            
            # 简单的HTML标签清理
            import re
            
            # 移除脚本和样式
            text = re.sub(r'<script[^>]*>.*?</script>', '', text, flags=re.DOTALL | re.IGNORECASE)
            text = re.sub(r'<style[^>]*>.*?</style>', '', text, flags=re.DOTALL | re.IGNORECASE)
            
            # 提取标题
            title_match = re.search(r'<title[^>]*>(.*?)</title>', text, re.IGNORECASE | re.DOTALL)
            title = title_match.group(1).strip() if title_match else ''
            
            # 移除HTML标签
            text = re.sub(r'<[^>]+>', ' ', text)
            
            # 解码HTML实体
            import html
            text = html.unescape(text)
            
            cleaned_text = await self._clean_text(text)
            
            # 提取元数据
            metadata = await self._extract_text_metadata(cleaned_text)
            metadata.update({
                'title': title,
                'format': 'html'
            })
            
            return {
                'success': True,
                'content': cleaned_text,
                'metadata': metadata
            }
            
        except Exception as e:
            logger.error(f"处理HTML文件失败: {e}")
            raise
    
    async def _process_json(self, content: Union[str, bytes], 
                          filename: Optional[str] = None) -> Dict[str, Any]:
        """处理JSON文件"""
        try:
            import json
            
            if isinstance(content, bytes):
                text = content.decode('utf-8')
            else:
                text = content
            
            # 解析JSON
            data = json.loads(text)
            
            # 将JSON转换为可读文本
            def json_to_text(obj, prefix=''):
                if isinstance(obj, dict):
                    parts = []
                    for key, value in obj.items():
                        if isinstance(value, (dict, list)):
                            parts.append(f"{prefix}{key}:")
                            parts.append(json_to_text(value, prefix + '  '))
                        else:
                            parts.append(f"{prefix}{key}: {value}")
                    return '\n'.join(parts)
                elif isinstance(obj, list):
                    parts = []
                    for i, item in enumerate(obj):
                        if isinstance(item, (dict, list)):
                            parts.append(f"{prefix}[{i}]:")
                            parts.append(json_to_text(item, prefix + '  '))
                        else:
                            parts.append(f"{prefix}[{i}]: {item}")
                    return '\n'.join(parts)
                else:
                    return str(obj)
            
            readable_text = json_to_text(data)
            cleaned_text = await self._clean_text(readable_text)
            
            # 提取元数据
            metadata = await self._extract_text_metadata(cleaned_text)
            metadata.update({
                'format': 'json',
                'json_keys': list(data.keys()) if isinstance(data, dict) else [],
                'json_type': type(data).__name__
            })
            
            return {
                'success': True,
                'content': cleaned_text,
                'metadata': metadata
            }
            
        except Exception as e:
            logger.error(f"处理JSON文件失败: {e}")
            raise
    
    async def _clean_text(self, text: str) -> str:
        """清理文本"""
        try:
            cleaned = text
            
            # 应用清理模式
            for pattern, replacement in self.clean_patterns:
                cleaned = re.sub(pattern, replacement, cleaned)
            
            return cleaned.strip()
            
        except Exception as e:
            logger.error(f"文本清理失败: {e}")
            return text
    
    async def _extract_text_metadata(self, text: str) -> Dict[str, Any]:
        """提取文本元数据"""
        try:
            metadata = {
                'length': len(text),
                'word_count': len(text.split()),
                'line_count': len(text.splitlines()),
                'paragraph_count': len([p for p in text.split('\n\n') if p.strip()])
            }
            
            # 语言检测
            try:
                if text.strip() and len(text) > 10:
                    language = detect(text[:1000])  # 只检测前1000字符
                    metadata['language'] = language
            except Exception:
                metadata['language'] = 'unknown'
            
            # 提取关键统计
            if text:
                # 平均句子长度
                sentences = re.split(r'[.!?。！？]', text)
                sentences = [s.strip() for s in sentences if s.strip()]
                if sentences:
                    avg_sentence_length = sum(len(s.split()) for s in sentences) / len(sentences)
                    metadata['avg_sentence_length'] = round(avg_sentence_length, 2)
                    metadata['sentence_count'] = len(sentences)
            
            return metadata
            
        except Exception as e:
            logger.error(f"提取文本元数据失败: {e}")
            return {'length': len(text)}
    
    def get_supported_formats(self) -> List[str]:
        """获取支持的文件格式列表"""
        return list(self.supported_formats.keys())
    
    async def validate_document(self, content: Union[str, bytes], 
                              max_size: int = 10 * 1024 * 1024) -> Tuple[bool, str]:
        """验证文档"""
        try:
            # 检查大小
            size = len(content)
            if size > max_size:
                return False, f"文档大小超过限制: {size} > {max_size}"
            
            # 检查内容
            if not content:
                return False, "文档内容为空"
            
            return True, "验证通过"
            
        except Exception as e:
            return False, f"验证失败: {e}"